#include <assert.h>
#include <arch.h>
#include <cpu.h>
#include <conio.h>
#include <string.h>
#include <paging.h>
#include <mman.h>
#include <context.h>
#include <types.h>

int context_init(context_t *ctx)
{
    void *kstack;
    uint32_t npages;
    uint32_t *kstacktop;

    if (ctx == NULL)
	return -1;

    npages = KSTACK_SIZE/PAGE_SIZE;
    memset(ctx, 0, sizeof(context_t));
    kstack = (void *)alloc_pages(npages);
    assert(kstack != NULL);
    if (kstack == NULL)
	return -1;

    bzero(kstack, KSTACK_SIZE);
    kstacktop = kstack + KSTACK_SIZE;
    ctx->kstack = kstacktop;		/* the top of kernel stack */
    ctx->stack0 = kstack;		/* base of kernel stack */
    ctx->ustack = NULL;
    
    ctx->stack0->user_eip = (uint32_t)&syscall_ret;

    return 0;
}

/*
 * todo: rewrite func to support more options!!!
 */
int context_setup(context_t *ctx, void *ustack, void (*entry) ())
{
    uint32_t *kstacktop;

    if (!ctx)
	return -1;

    assert(ctx->kstack != NULL);
    
    kstacktop = ctx->kstack;
    // for user context
    if (ustack) {	/* user task */
	*(--kstacktop) = SEL_UDATA;			/* User SS */
	*(--kstacktop) = (uint32_t)ustack + PAGE_SIZE;	/* User ESP (stack size is page) */
	*(--kstacktop) = EFLAGS_IF3;			/* eflags */
	*(--kstacktop) = SEL_UCODE;			/* CS */
	if (entry)
	    *(--kstacktop) = (uint32_t)entry;		/* EIP */
	else
	    kstacktop--;
	kstacktop -= 8;

	*(--kstacktop) = SEL_UDATA;	/* DS */
	*(--kstacktop) = SEL_UDATA;	/* ES */
	*(--kstacktop) = SEL_UDATA;	/* FS */
	*(--kstacktop) = SEL_UDATA;	/* GS */           
    } else {	/* kernel task */
	*(--kstacktop) = 0;
	*(--kstacktop) = EFLAGS_IF0;		/* eflags */
	*(--kstacktop) = SEL_KCODE;		/* CS */
	if (entry)
	    *(--kstacktop) = (uint32_t)entry;	/* EIP */
	else
	    kstacktop--;
	kstacktop -= 8;

	*(--kstacktop) = SEL_KDATA;	/* DS */
	*(--kstacktop) = SEL_KDATA;	/* ES */
	*(--kstacktop) = SEL_KDATA;	/* FS */
	*(--kstacktop) = SEL_KDATA;	/* GS */
    }

    ctx->stack0 = (userframe_t *)kstacktop;	/* real esp of context */
    ctx->ustack = ustack;

    return 0;
}

int context_destroy(context_t *ctx)
{
    int error = 0;

    if (ctx == NULL)
	return -1;

    if (ctx->kstack)	/* top of kernel stack */
	free_pages((uint32_t)ctx->kstack - KSTACK_SIZE, (uint32_t)ctx->kstack);

    return (error);
}
